#include <basecode/glfwapp.h>

#include "fluid.h"
#include <gl\SOIL.h>

class MyApp : public GLFWApp
{
private:
	FluidSolver *solver;

public:
	MyApp() : GLFWApp()
	{
		init();
		mainloop();
	}

	void init()
	{
		solver = new FluidSolver();
		glfwSetWindowSize( solver->SIM_W, solver->SIM_W);
		physicsFps = 0;
		onePhySec = 0.0;
		physicsFrames = 0;
		lastsimulate = 0.0;
		phys_millis = 0.0,

		renderFps = 0;
		oneRenderSec = 0.0;
		renderFrames = 0;


		// Init Point Sprite
//#define use_point_sprite
#ifdef use_point_sprite
		float sizes[2];
		glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, sizes);
		glEnable( GL_POINT_SPRITE );
		glPointParameterfARB( GL_POINT_SIZE_MAX_ARB, sizes[1] );
		glPointParameterfARB( GL_POINT_SIZE_MIN_ARB, sizes[0]);
		
		float quadratic[] =  { 0.0f, 0.0f, 0.0f };
		//glPointParameterfvARB( GL_POINT_DISTANCE_ATTENUATION_ARB, quadratic );
		glTexEnvi( GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE ); //Generates tex coords for Point 	

		glActiveTexture(GL_TEXTURE0);
		glEnable( GL_TEXTURE_2D );

		int image_width, image_height, channels;
		unsigned char* image_data = SOIL_load_image( "particle.png", &image_width, &image_height, &channels, SOIL_LOAD_RGB);
		if( image_data != NULL ){
			glEnable(GL_TEXTURE_2D);
			glGenTextures(1, &g_textureID);
			glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
			glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR );
			glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR );
			glTexImage2D(GL_TEXTURE_2D, 0, 3, image_width, image_height, 0,
				GL_RGB, GL_UNSIGNED_BYTE, image_data);
			SOIL_free_image_data(image_data);
		}else{
			printf("couldn't load img\n");
		}
#endif

	}

	void update()
	{
		
		static double desiredFrameTime = 1.0 / 200.0;

		if( Input::MouseDown(GLFW_MOUSE_BUTTON_1) )
		{
			solver->activateAttractor( Input::mouseX(), Input::mouseY() );
		} else if ( !Input::MouseDown(GLFW_MOUSE_BUTTON_1) && solver->attracting )
		{
			solver->deactivateAttractor();
		}

		if( Input::MouseDown(GLFW_MOUSE_BUTTON_2) )
		{
			solver->activateRepelling( Input::mouseX(), Input::mouseY() );
		} else if ( !Input::MouseDown(GLFW_MOUSE_BUTTON_2) && solver->repelling )
		{
			solver->deactivateAttractor();
		}

		static double cooldown = 0;
		cooldown -= dt;
		bool canAddParticle = Input::keyDown(GLFW_KEY_SPACE) && cooldown <= 0.0;
		if (canAddParticle)
		{
			double particlesPerSecond = 100.0;
			cooldown = 1.0/particlesPerSecond;
			solver->addParticles( Input::mouseX(), Input::mouseY() );
		}
		if (Input::keyDown(GLFW_KEY_DEL)){ solver->removeAllParticles(); }
		if (Input::keyDown('R')){ solver->initParticles(); }

		//if (Input::keyDown('I')) { solver->reinstallShader(); }

		if (Input::keyHit('T')) { 
			solver->do_viscosity_step = !solver->do_viscosity_step; 
			printf("viscosity toggled %i\n", solver->do_viscosity_step);
		}
		if (Input::keyHit('P')) { 
			solver->togglePause();
		}

		double time_before_update = glfwGetTime();
		bool time_to_update = (time_before_update - lastsimulate) > desiredFrameTime;
		if (time_to_update) {			
			
			solver->update(); 
			double time_after = glfwGetTime();
			lastsimulate = time_before_update;
			phys_millis = time_after - time_before_update;

			if( time_before_update > onePhySec )
			{
				physicsFps = physicsFrames;
				physicsFrames = 0;
				onePhySec = time_before_update + 1.0;
			}
			physicsFrames++;
			
		}
	}
	
	void render()
	{
		static double lastrender = 0;
		static double desiredFrameTime = 1.0 / 60.0;

		double now = glfwGetTime();
		bool timetorender = (now - lastrender) > desiredFrameTime;
		if( timetorender )
		{
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
			glfwSwapInterval(0);
			setOrthoProjection();

			solver->render();

			char title[256];
			//char* all_systems_go = " | phyfps>199 :)";
			//char* cant_keep_up = " | pc can't keep up with sim phyfps<199";
			//char *msg = NULL;
			//if( physicsFps > 199 ){
			//	msg = all_systems_go;
			//}else{
			//	msg = cant_keep_up;
			//}

			sprintf_s(title, 256, "physics ms: %.3f, phyfps = %i, particles = %i, upload: %f", phys_millis*1000, physicsFps, solver->getParticleCount(), solver->uploadTime*1000);
			glfwSetWindowTitle( title );

			lastrender = glfwGetTime();
			glfwSwapBuffers();

			if( now > oneRenderSec )
			{
				renderFps = renderFrames;
				renderFrames = 0;
				oneRenderSec = now + 1.0;
			}
			renderFrames++;
		}


	}

private:
	int physicsFps;
	double onePhySec;
	int physicsFrames;
	double lastsimulate, phys_millis;

	int renderFps;
	double oneRenderSec;
	int renderFrames;

	GLuint g_textureID;
};